#include <errno.h>
#include <stddef.h>
#include <unistd.h>
#include <sys/mman.h>

#include "fibthread.h"

extern long page_size;

/* External function definitions */

/** Initialization **/

int fibthread_attr_destroy(fibthread_attr_t *attr)
{ attr->initialized = FIBTHREAD_ATTR_DESTROYED; return 0; }

int fibthread_attr_init(fibthread_attr_t *attr)
{
	if (attr->initialized == FIBTHREAD_ATTR_INITIALIZED)
		return EBUSY;

	attr->sched_policy = FIBTHREAD_SCHED_POLICY_DEFAULT;
	attr->sched_param.sched_priority = FIBTHREAD_SCHED_PRIORITY_DEFAULT;
	attr->detach_state = FIBTHREAD_CREATE_JOINABLE;
	attr->stack_size = FIBTHREAD_STACK_DEFAULT;
	attr->guard_size = FIBTHREAD_GUARD_DEFAULT;
	attr->initialized = FIBTHREAD_ATTR_INITIALIZED;
	attr->stack_addr = NULL;

	return 0;
}

/** Detachment **/

int fibthread_attr_getdetachstate(const fibthread_attr_t *attr, int *detach_state)
{
	*detach_state = attr->detach_state;

	return 0;
}

int fibthread_attr_setdetachstate(fibthread_attr_t *attr, int detach_state)
{
	if (detach_state != FIBTHREAD_CREATE_DETACHED && detach_state != FIBTHREAD_CREATE_JOINABLE)
		return EINVAL;

	attr->detach_state = detach_state;

	return 0;
}

/** Stack guard **/

int fibthread_attr_getguardsize(const fibthread_attr_t *attr, size_t *guard_size)
{
	*guard_size = attr->guard_size;

	return 0;
}

int fibthread_attr_setguardsize(fibthread_attr_t *attr, size_t guard_size)
{
	attr->guard_size = guard_size;

	return 0;
}

/** Scheduling parameter **/

int fibthread_attr_getschedparam(const fibthread_attr_t *attr, struct fibsched_param *param)
{
	param->sched_priority = attr->sched_param.sched_priority;

	return 0;
}

int fibthread_attr_setschedparam(fibthread_attr_t *attr, const struct fibsched_param *param)
{
	if (!param)
		return EINVAL;

	if (attr->sched_policy == FIBTHREAD_SCHED_FIFO) {
		if (param->sched_priority < FIBTHREAD_SCHED_FIFO_PRIO_MIN || param->sched_priority > FIBTHREAD_SCHED_FIFO_PRIO_MAX)
			return ENOTSUP;
	}
	else /* if (attr->sched_policy == FIBTHREAD_SCHED_FIFO) */ {
		if (param->sched_priority < FIBTHREAD_SCHED_RR_PRIO_MIN || param->sched_priority > FIBTHREAD_SCHED_RR_PRIO_MAX)
			return ENOTSUP;
	}

	return 0;
}

/** Policy **/

int fibthread_attr_getschedpolicy(const fibthread_attr_t *attr, int *policy)
{
	*policy = attr->sched_policy;

	return 0;
}

int fibthread_attr_setschedpolicy(fibthread_attr_t *attr, int policy)
{
	if (attr->sched_policy != FIBTHREAD_SCHED_FIFO && attr->sched_policy != FIBTHREAD_SCHED_RR)
		return EINVAL;

	attr->sched_policy = policy;

	return 0;
}

/** Stack **/

int fibthread_attr_getstack(const fibthread_attr_t *attr, void **stack_addr, size_t *stack_size)
{
	*stack_addr = attr->stack_addr;
	*stack_size = attr->stack_size;

	return 0;
}

int fibthread_attr_setstack(fibthread_attr_t *attr, void *stack_addr,  size_t stack_size)
{
	int retval;

	if ((retval = initialize()) != 0)
		return retval;

	if (stack_size < FIBTHREAD_STACK_MIN || (ptrdiff_t)stack_addr % page_size || stack_size % page_size)
		return EINVAL;

	attr->stack_addr = stack_addr;
	attr->stack_size = stack_size;

	return 0;
}

int fibthread_attr_getstacksize(const fibthread_attr_t *attr, size_t *stack_size)
{
	*stack_size = attr->stack_size;

	return 0;
}

int fibthread_attr_setstacksize(fibthread_attr_t *attr, size_t stack_size)
{
	/* If the stack value is determined by the client; than it shall be rounded up during fiber creation */

	if (stack_size < FIBTHREAD_STACK_MIN)
		return EINVAL;

	attr->stack_size = stack_size;

	return 0;
}
